#include "bsp.h"
#include <esp_mac.h>
#include <esp_random.h>
#include <cJSON.h>
#include <esp_system.h>
#include <esp_chip_info.h>
#include <esp_app_desc.h>
#include <esp_partition.h>
#include <esp_ota_ops.h>
#include <esp_psram.h>
#include <esp_flash.h>
#include "nvs_settings.h"


static char *uuid = NULL;
static char *mac_addr = NULL;

void bsp_init(void)
{
    bsp_led_init();
    bsp_led_set_state(LED_STATE_STARTING);

    bsp_codec_init();
    bsp_codec_set_mic_gain(10);
    bsp_codec_set_volume(60);

    bsp_wifi_init();
}

const char *bsp_get_mac_address(void)
{
    if (mac_addr)
    {
        return mac_addr;
    }

    mac_addr = malloc(18);
    assert(mac_addr);
    // 首先查询mac地址
    uint8_t mac[6];
    ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_STA));
    snprintf(mac_addr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return mac_addr;
}

const char *bsp_get_uuid(void)
{
    if (uuid)
    {
        return uuid;
    }

    // 尝试读取UUID
    settings_t *settings = nvs_settings_create();
    uuid = nvs_settings_get_string(settings, "uuid");
    if (uuid)
    {
        nvs_settings_free(settings);
        return uuid;
    }

    // 生成新的UUID并保存到FLASH
    uuid = malloc(37);
    assert(uuid);
    uint8_t bytes[16];
    for (int i = 0; i < 16; i++)
    {
        bytes[i] = esp_random() & 0xFF;
    }
    // 设置版本和变体
    bytes[6] = (bytes[6] & 0x0F) | 0x40; // version 4
    bytes[8] = (bytes[8] & 0x3F) | 0x80; // variant 10

    snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             bytes[0], bytes[1], bytes[2], bytes[3],
             bytes[4], bytes[5], bytes[6], bytes[7],
             bytes[8], bytes[9], bytes[10], bytes[11],
             bytes[12], bytes[13], bytes[14], bytes[15]);

    // 保存到FLASH
    nvs_settings_set_string(settings, "uuid", uuid);
    nvs_settings_free(settings);

    return uuid;
}

char *bsp_get_json(void)
{
    cJSON *root = cJSON_CreateObject();
    assert(root);
    cJSON_AddStringToObject(root, "uuid", bsp_get_uuid());
    cJSON_AddNumberToObject(root, "version", 2);

    // TODO)) 动态获取PSRAM大小
    cJSON_AddNumberToObject(root, "psram_size", esp_psram_get_size());
    cJSON_AddStringToObject(root, "chip_model_name", "esp32s3");
    cJSON_AddNumberToObject(root, "flash_size", 8 * 1024 * 1024);
    cJSON_AddStringToObject(root, "mac_address", bsp_get_mac_address());
    cJSON_AddNumberToObject(root, "minimum_free_heap_size", esp_get_minimum_free_heap_size());

    // application
    cJSON *application = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "application", application);

    const esp_app_desc_t *app_desc = esp_app_get_description();
    cJSON_AddStringToObject(application, "compile_time", app_desc->date);
    cJSON_AddStringToObject(application, "version", app_desc->version);
    cJSON_AddStringToObject(application, "name", app_desc->project_name);
    cJSON_AddStringToObject(application, "idf_version", app_desc->idf_ver);
    cJSON_AddStringToObject(application, "elf_sha256", esp_app_get_elf_sha256_str());

    // board
    cJSON *board = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "board", board);

    cJSON_AddNumberToObject(board, "chanel", BSP_CODEC_CHANNEL_COUNT);
    cJSON_AddStringToObject(board, "ip", "192.168.1.100");
    cJSON_AddStringToObject(board, "ssid", "atguigu");
    cJSON_AddNumberToObject(board, "rssi", -50);
    cJSON_AddStringToObject(board, "mac", bsp_get_mac_address());
    cJSON_AddStringToObject(board, "name", "bread-compact-wifi");
    cJSON_AddStringToObject(board, "type", "bread-compact-wifi");

    // chip_info
    cJSON *chip_info = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "chip_info", chip_info);

    esp_chip_info_t chip;
    esp_chip_info(&chip);
    cJSON_AddNumberToObject(chip_info, "model", chip.model);
    cJSON_AddNumberToObject(chip_info, "features", chip.features);
    cJSON_AddNumberToObject(chip_info, "cores", chip.cores);
    cJSON_AddNumberToObject(chip_info, "revision", chip.revision);

    // partition_table
    cJSON *partition_table = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "partition_table", partition_table);

    esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
    while (it)
    {
        const esp_partition_t *curr_par = esp_partition_get(it);
        // 处理这个partition信息
        cJSON *partition = cJSON_CreateObject();
        cJSON_AddItemToArray(partition_table, partition);

        cJSON_AddNumberToObject(partition, "address", curr_par->address);
        cJSON_AddNumberToObject(partition, "size", curr_par->size);
        cJSON_AddNumberToObject(partition, "type", curr_par->type);
        cJSON_AddNumberToObject(partition, "subtype", curr_par->subtype);
        cJSON_AddStringToObject(partition, "label", curr_par->label);

        // 迭代
        it = esp_partition_next(it);
    }
    esp_partition_iterator_release(it);

    // ota模块
    const esp_partition_t *running_partition = esp_ota_get_running_partition();
    cJSON *ota = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "ota", ota);
    cJSON_AddStringToObject(ota, "label", running_partition->label);

    char *json_str = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    return json_str;
}
